| block.id | time | attacker | target | profit | currency |
|---|---|---|---|---|---|
| 1763386 | 2022-04-14T22:33:36.609 | t4PVii9P… | qHrEVyEY… | 5.101147 | AUSD |
| 1764563 | 2022-04-15T05:58:18.608 | t4PVii9P… | qXmxifJ5… | 7.743719 | AUSD |
| 1775030 | 2022-04-18T00:46:42.231 | t4PVii9P… | qtRgxP95… | 9.295307 | AUSD |
| 1775043 | 2022-04-18T00:51:48.525 | t4PVii9P… | qtRgxP95… | 8.108215 | AUSD |
| 1779201 | 2022-04-19T03:02:48.275 | t4PVii9P… | sZLSbtg4… | 9.258678 | AUSD |
| 1783201 | 2022-04-20T04:39:12.272 | t4PVii9P… | oYVonSHV… | 56.564416 | AUSD |
| 1786130 | 2022-04-20T22:42:42.612 | t4PVii9P… | qtRgxP95… | 8.588095 | AUSD |
| 1792992 | 2022-04-22T16:25:54.418 | t4PVii9P… | pyXki2UU… | 5.627787 | AUSD |
| 1793642 | 2022-04-22T20:28:06.302 | t4PVii9P… | pyXki2UU… | 13.875839 | AUSD |
| 1794246 | 2022-04-23T00:09:48.538 | t4PVii9P… | tWZQqSgj… | 8.091017 | AUSD |
| 1795231 | 2022-04-23T06:12:30.493 | t4PVii9P… | qtRgxP95… | 10.597481 | AUSD |
| 1796525 | 2022-04-23T14:02:12.421 | t4PVii9P… | qtRgxP95… | 8.380745 | AUSD |
| 1797229 | 2022-04-23T18:17:42.39 | t4PVii9P… | rXBgtiMf… | 14.244646 | AUSD |
| 1797302 | 2022-04-23T18:45:30.169 | t4PVii9P… | qtRgxP95… | 6.856932 | AUSD |
| 1797618 | 2022-04-23T20:40:30.172 | t4PVii9P… | qtRgxP95… | 8.444492 | AUSD |
| 1797951 | 2022-04-23T22:44:30.267 | t4PVii9P… | p3SKGrRH… | 12.334130 | AUSD |
| 1799420 | 2022-04-24T07:32:42.22 | t4PVii9P… | tGzni538… | 26.592624 | AUSD |
| 1799443 | 2022-04-24T07:40:54.357 | t4PVii9P… | qtRgxP95… | 17.115859 | AUSD |
| 1799469 | 2022-04-24T07:49:42.529 | t4PVii9P… | qtRgxP95… | 8.708309 | AUSD |
| 1799567 | 2022-04-24T08:23:12.195 | t4PVii9P… | qtRgxP95… | 18.885492 | AUSD |
| 1799708 | 2022-04-24T09:16:00.243 | t4PVii9P… | tnq3xJ7L… | 7.787859 | AUSD |
| 1800092 | 2022-04-24T11:36:18.285 | t4PVii9P… | qtRgxP95… | 5.784155 | AUSD |
| 1801530 | 2022-04-24T20:19:18.571 | t4PVii9P… | qtRgxP95… | 8.230302 | AUSD |
| 1801835 | 2022-04-24T22:17:24.514 | t4PVii9P… | qtRgxP95… | 5.345694 | AUSD |
| 1802423 | 2022-04-25T01:55:18.652 | t4PVii9P… | rcGWYXGo… | 5.641653 | AUSD |
| 1802775 | 2022-04-25T04:04:24.258 | t4PVii9P… | qHfZ4qC8… | 9.395342 | AUSD |
| 1803067 | 2022-04-25T05:47:00.407 | t4PVii9P… | qXmxifJ5… | 27.876923 | AUSD |
| 1803296 | 2022-04-25T07:06:30.338 | t4PVii9P… | pGTcwV7j… | 37.777922 | AUSD |
| 1803318 | 2022-04-25T07:13:12.657 | t4PVii9P… | pGTcwV7j… | 37.348302 | AUSD |
| 1805456 | 2022-04-25T20:07:42.358 | t4PVii9P… | rscV8fRt… | 28.062067 | AUSD |
| 1805613 | 2022-04-25T21:03:12.277 | t4PVii9P… | pDubmCs2… | 11.533055 | AUSD |
| 1807182 | 2022-04-26T06:38:12.411 | t4PVii9P… | ooDwe5pD… | 5.741824 | AUSD |
| 1809508 | 2022-04-26T20:44:24.388 | t4PVii9P… | ocuC8zru… | 5.602152 | AUSD |
| 1811767 | 2022-04-27T10:20:24.358 | t4PVii9P… | qtRgxP95… | 8.164931 | AUSD |
| 1814659 | 2022-04-28T03:44:24.35 | t4PVii9P… | rnHVjvTT… | 17.462503 | AUSD |
| 1816054 | 2022-04-28T12:05:42.562 | t4PVii9P… | oXgHePRW… | 8.268756 | AUSD |
| 1816094 | 2022-04-28T12:19:30.379 | t4PVii9P… | oXgHePRW… | 5.406004 | AUSD |
| 1816384 | 2022-04-28T14:06:18.541 | t4PVii9P… | pwt9PzWE… | 7.321414 | AUSD |
| 1817713 | 2022-04-28T22:15:24.31 | t4PVii9P… | qHrEVyEY… | 7.542795 | AUSD |
| 1828106 | 2022-05-01T12:08:48.209 | t4PVii9P… | pwt9PzWE… | 17.644899 | AUSD |
| 1828188 | 2022-05-01T12:36:24.523 | t4PVii9P… | rXBgtiMf… | 9.593515 | AUSD |
| 1829970 | 2022-05-01T22:59:02.706 | t4PVii9P… | qtRgxP95… | 9.114611 | AUSD |
| 1833224 | 2022-05-02T18:50:42.616 | tuxT4Xsi… | tqLX4Syb… | 4.950172 | KAR |
| 1834561 | 2022-05-03T03:02:42.335 | t4PVii9P… | pwt9PzWE… | 6.198720 | AUSD |
| 1834952 | 2022-05-03T05:27:42.746 | t4PVii9P… | pwt9PzWE… | 13.877820 | AUSD |
| 1840006 | 2022-05-04T12:03:12.325 | t4PVii9P… | oXgHePRW… | 12.131904 | AUSD |
| 1840617 | 2022-05-04T15:45:18.534 | t4PVii9P… | sqRCxEaD… | 15.210450 | AUSD |
| 1840923 | 2022-05-04T17:33:24.5 | t4PVii9P… | odukVAMP… | 15.447788 | AUSD |
| 1846226 | 2022-05-06T00:31:06.412 | t4PVii9P… | raAdnAUJ… | 10.731251 | AUSD |
| 1849005 | 2022-05-06T16:42:19.013 | t4PVii9P… | rUGMtr8o… | 69.989384 | AUSD |
| 1855646 | 2022-05-08T06:52:48.227 | t4PVii9P… | u1BSDbT8… | 11.676573 | AUSD |
| 1863150 | 2022-05-10T00:44:18.34 | t4PVii9P… | rcGWYXGo… | 52.804447 | AUSD |
| 1864162 | 2022-05-10T06:05:50.711 | t4PVii9P… | sqRCxEaD… | -18.391052 | AUSD |
| 1870025 | 2022-05-11T08:23:12.679 | tuxT4Xsi… | rh7zjB7E… | 30.104430 | KAR |
| 1870452 | 2022-05-11T10:05:48.269 | t4PVii9P… | u1BSDbT8… | 13.677420 | AUSD |
| 1873271 | 2022-05-11T21:18:42.633 | t4PVii9P… | rUGMtr8o… | 13.072383 | AUSD |
| 1874950 | 2022-05-12T03:46:12.402 | t4PVii9P… | qJ6pUk4k… | 7.999504 | AUSD |
| 1875986 | 2022-05-12T07:49:48.57 | t4PVii9P… | oQn2B1LK… | -14.995782 | AUSD |
| 1875996 | 2022-05-12T07:51:54.522 | t4PVii9P… | rWsv8gne… | 10.847079 | AUSD |
| 1878762 | 2022-05-12T18:37:06.604 | t4PVii9P… | tZfhx6NZ… | 10.735460 | AUSD |
Last updated: 2022-05-13 09:35:21
Date range of data: 2022-04-13T00:18:54.324 to 2022-05-12T23:52:12.263.
Sources:
Swaps:
---
title: "Acala / Karura Sandwich Attack Dashboards"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
social: menu
source_code: embed
params:
network: Karura
window: 30
---
```{css custom1, echo=FALSE}
.dataTables_scrollBody {
max-height: 100% !important;
}
```
```{r global, include=FALSE}
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE,
comment = "#>"
)
library(kableExtra)
library(formattable)
library(lubridate)
library(flexdashboard)
library(DT)
library(dygraphs)
# Helper function to concat
`%+%` <- function(a, b) paste0(a, b)
# remotes::install_github("ropensci/ghql") # if package is not already installed
library(jsonlite)
library(data.table)
library(ghql)
x <- GraphqlClient$new()
window <- params$window
endpoint <- params$endpoint
network <- params$network
if (network == 'Acala') {
dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala-dex"
loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-loan-subql"
swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-swap-day-data"
official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala"
} else {
dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura-dex"
loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-loan-subql"
swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-test"
official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura"
}
getSwapsDex <- function(endpoint, window) {
# endpoint <- dex_endpoint; window <- 10
# make a client
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:1000) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query('swaps', '
{
query {
swaps (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node {
id address {
id
} pool {
id
} token0 {
id
} token1 {
id
} token0InAmount token1OutAmount
tradePath price0 price1 block {
id
} extrinsic {
id
} timestamp
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries$swaps) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query$swaps$pageInfo$endCursor
res <- as.data.table(result$data$query$swaps$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query$swaps$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList)
setnames(res, old = names(res), new = gsub("node.", "", names(res)))
if (substr(max(res$timestamp), 12, 13) < 23) {
maxdate <- as.Date(max(res$timestamp))-1
} else {
maxdate <- as.Date(max(res$timestamp))
}
res <- res[timestamp <= maxdate]
res[, date := as.Date(timestamp)]
setorder(res, timestamp)
# Replace foreign assets
res[, token0.id := subscanr::fixToken(token0.id)]
res[, token1.id := subscanr::fixToken(token1.id)]
# Normalize pairs
res[, pair := paste0(token0.id %+% ":" %+% token1.id)]
res[token1.id < token0.id, pair := paste0(token1.id %+% ":" %+% token0.id)]
res[, exclude := token0.id == token1.id]
res
}
# queries ####
# swaps2 <- getDailyPoolsQuery(dex_endpoint, window)
swaps <- getSwapsDex(dex_endpoint, window)
swaps <- merge(swaps, subscanr::tokens, by.x='token0.id', by.y='Token') %>% setnames("decimals","decimals0")
swaps[, Name := NULL]
swaps <- merge(swaps, subscanr::tokens, by.x='token1.id', by.y='Token') %>% setnames("decimals","decimals1")
swaps[, Name := NULL]
swaps[, adj0 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals0) + 1))]
swaps[, adj1 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals1) + 1))]
swaps[, token0InAmount := as.numeric(token0InAmount)]
swaps[, token1OutAmount := as.numeric(token1OutAmount)]
swaps[, price0 := as.numeric(price0) / 1e18]
swaps[, price1 := as.numeric(price1) / 1e18]
swaps[, amount0 := token0InAmount / adj0]
swaps[, amount1 := token1OutAmount / adj1]
swaps[, token0.id := subscanr::fixToken(token0.id)]
swaps[, token1.id := subscanr::fixToken(token1.id)]
swaps[, tradePath := subscanr::fixToken(tradePath)]
swaps[, pathLength := length(strsplit(tradePath, ",")[[1]]) - 1, by = id]
swaps[, volume0USD := amount0 * price0]
swaps[, volume1USD := amount1 * price1]
swaps[, volumeUSDFloat := (volume0USD + volume1USD) / 2]
swaps[volume0USD == 0 & volume1USD > 0, volumeUSDFloat := volume1USD]
swaps[volume1USD == 0 & volume0USD > 0, volumeUSDFloat := volume0USD]
mysort <- function(a, b) ifelse(a < b, a %+% ":" %+% b, b %+% ":" %+% a)
getPath <- function(tradePath) {
# tradePath <- swaps[1]$tradePath
tp <- strsplit(tradePath, ",")[[1]]
n <- length(tp) - 1
if (n == 3) {
return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), mysort(tp[3],tp[4])))
} else if (n == 2) {
return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), "NA:NA"))
}
list(mysort(tp[1],tp[2]), "NA:NA", "NA:NA")
}
swaps[, c("pair1", "pair2", "pair3") := getPath(tradePath), by = id]
swaps[, fee := volumeUSDFloat * .03]
swaps[, feeAdj := volumeUSDFloat * .03 * pathLength]
setnames(swaps, "address.id", "accountId")
index <- strsplit(swaps$id, "-")
index <- do.call("rbind", index)
myindex <- as.numeric(index[, 2])
swaps[, index := myindex]
setorder(swaps, block.id, index)
starti <- min(swaps$block.id) #
endi <- max(swaps$block.id)
j <- 1
d <- list()
prof <- list()
for (i in starti:endi) {
# i <- 1829970
tmp <- swaps[block.id == i]
tmpn <- tmp[, .N, by = 'accountId']
# Look for the same account number 2 times in the same block
if (nrow(tmpn) >= 2 && max(tmpn$N) >= 2) {
acct <- tmp[, .N, by = 'accountId'][N >= 2, accountId]
for (account in acct) {
# account <- acct[1]
for (ii in 1:(nrow(tmp) - 2)) {
if (tmp$token0.id[ii] == tmp$token1.id[ii+2] &&
tmp$token1.id[ii] == tmp$token0.id[ii+2] &&
tmp$accountId[ii] == tmp$accountId[[ii+2]] &&
tmp$accountId[ii] != tmp$accountId[[ii+1]] &&
length(grep(tmp$token0.id[ii], tmp$tradePath[ii+1])) > 0 &&
length(grep(tmp$token1.id[ii], tmp$tradePath[ii+1])) > 0) {
d[[j]] <- tmp[, .(block.id, index, token0.id, token1.id, tradePath, token0InAmount, token1OutAmount, price0, price1, timestamp, accountId)][ii:(ii+2)]
prof[[j]] = data.table(block.id = tmp$block.id[ii], time = tmp$timestamp[ii], attacker = tmp$accountId[ii], target = tmp$accountId[ii+1], profit = as.numeric(tmp$token1OutAmount[ii+2]) - as.numeric(tmp$token0InAmount[ii]), currency = tmp$token0.id[ii])
j <- j + 1
}
} # end for ii
} # end for account
}
}
out <- rbindlist(d)
p <- rbindlist(prof)
output <- unique(p)
output[, profit := profit / 1e12]
output[, attacker := substr(attacker, 1, 8) %+% "..."]
output[, target := substr(target, 1, 8) %+% "..."]
```
### `r network` sandwich attacks in the past 30 days
```{r plot}
dygraph(output[, .N, by = as.Date(time)], main = network %+% " Sandwich Attacks") %>%
dyStackedBarChart()
```
Row
---
### Raw Data
```{r table}
knitr::kable(output, escape = FALSE) %>%
kable_styling()
```
Row
---
### Sources:
Last updated: `r Sys.time()`
Date range of data: `r min(swaps$timestamp)` to `r max(swaps$timestamp)`.
Sources:
* [SubQuery Network](https://explorer.subquery.network/)
Swaps:
* [Acala-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/acala-swap-day-data)
* [Karura-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/karura-swap-day-data)